<?php
/**
 * Created by PhpStorm
 * User: 龍zero
 * Date: 2022-04-10
 * Time: 16:28
 */
declare(strict_types=1);

namespace App\Service;


use App\Exception\AuthenticationException;
use App\Exception\ForbiddenException;
use App\Exception\NotFoundException;
use App\Exception\ParameterException;
use App\Lib\Enum\GroupLevel;
use App\Lib\Tools\LocalUser;
use App\Lib\Tools\Paginator;
use App\Model\LinGroup;
use App\Model\LinPermission;
use App\Model\LinUser;
use App\Model\LinUserGroup;
use App\Model\LinUserIdentity;
use EasySwoole\VerifyCode\Config;
use EasySwoole\VerifyCode\VerifyCode;
use Hyperf\Context\ApplicationContext;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;
use Hyperf\Snowflake\IdGeneratorInterface;

class UserService
{
    /**
     * @Inject()
     * @var UserIdentityService
     */
    private $userIdentityService;

    /**
     * @Inject()
     * @var GroupService
     */
    private $groupService;

    /**
     * @Inject()
     * @var PermissionService
     */
    private $permissionService;

    /**
     * @Inject
     * @var Redis
     */
    private $redis;

    private $key = 'captchaId:';

//    /**
//     * 自定义分页返回对象
//     * @Inject()
//     * @var Paginator
//     */
//    private $paginator;

    public function createUser(array $params)
    {

        #查询是否有重复用户名
        $user = LinUser::where('username',$params['username'])->first();
        if ($user) {
            throw new ForbiddenException(10071);
        }
        if (isset($params['email']) && $params['email']) {
            $user = LinUser::where('email',$params['email'])->first();
            if ($user) {
                throw new ForbiddenException(10076);
            }
        }
        Db::beginTransaction();
        try {
            $linUser = new LinUser();
            $linUser->username = $params['username'];
            $linUser->nickname = $params['nickname'] ?? $params['username'];
            $linUser->avatar = $params['avatar'] ?? '';
            $linUser->email = $params['email'] ?? '';
            $linUser->save();
            if ($params['group_ids']) {
                if(!in_array(GroupLevel::ROOT,$params['group_ids'])){
                    $groups = LinGroup::whereIn('id', $params['group_ids'])->get();
                    $insert = [];
                    foreach ($groups as $group) {
                        $insert[] = [
                            'user_id' => $linUser->id,
                            'group_id' => $group->id
                        ];
                    }
                    LinUserGroup::insert($insert);
                }else{
                    #权限组里有超级管理员 不写入其他分组权限
                    $group = LinGroup::where('level', GroupLevel::ROOT)->first();
                    $linUserGroup = new LinUserGroup();
                    $linUserGroup->user_id = $linUser->id;
                    $linUserGroup->group_id = $group->id;
                    $linUserGroup->save();
                }
            } else {
                $group = LinGroup::where('level', GroupLevel::GUEST)->first();
                $linUserGroup = new LinUserGroup();
                $linUserGroup->user_id = $linUser->id;
                $linUserGroup->group_id = $group->id;
                $linUserGroup->save();
            }
            $this->userIdentityService->createUsernamePasswordIdentity($linUser->id, $params['username'], $params['password']);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollBack();
            throw $e;
        }

    }


    public function getUserByUsername(string $username,string $password)
    {
        $user = LinUser::where('username', $username)
            ->first();
        if (!$user) {
            throw new ParameterException(10021);
        }
        if (!$this->userIdentityService->verifyUsernamePassword($user->id, $username, $password)) {
            throw new ParameterException(10031);
        }
        return $user;
    }


    public function getUserPermissions($userId){
        $permissionIds = [];
        $groups = LinUserGroup::where('user_id',$userId)->get();
        foreach ($groups as $group){
            $_permissionIds = $group->permissions->columns('permission_id')->toArray();
            $permissionIds = array_unique(array_merge($_permissionIds,$permissionIds));
        }
        if(!$permissionIds){
            throw new AuthenticationException(10001);
        }
        return LinPermission::whereIn('id',$permissionIds)->where('mount',1)->get();
    }

    public function getUserPageByGroupId($groupId,int $count,int $page){
        $rootGroupId = $this->groupService->getParticularGroupIdByLevel(GroupLevel::ROOT);
        if($groupId == ''){
            $userIds = LinUserGroup::where('group_id','<>',$rootGroupId)->pluck('user_id')->toArray();
        }else{
            $userIds = LinUserGroup::where('group_id',$groupId)
                ->where('group_id','<>',$rootGroupId)
                ->pluck('user_id')->toArray();
        }
//        $query = LinUser::whereIn('id',$userIds);
//        return $this->paginator->page($query,$count,$page);
        return  LinUser::whereIn('id',$userIds)->paginate((int)$count,['*'],'page',(int)$page);
    }

    public function getRootUserIds(){
        $rootGroupId = $this->groupService->getParticularGroupIdByLevel(GroupLevel::ROOT);
        return LinUserGroup::where('group_id','=',$rootGroupId)->pluck('user_id')->toArray();
    }

    public function removeById($id){
        #刪除user、userIdentity、userGroup
        Db::beginTransaction();
        try {
            $user = LinUser::query()->find($id);
            $user->delete();
            LinUserIdentity::query()->where('user_id',$id)->delete();
            LinUserGroup::query()->where('user_id',$id)->delete();
            Db::commit();
        }catch (\Exception $e){
            Db::rollBack();
            throw $e;
        }
    }

    public function getStructuralUserPermissions($userId){
        $groupIds = LinUserGroup::where('user_id',$userId)->get('group_id')->columns(['group_id'])->toArray();
        $permissions = $this->permissionService->getPermissionByGroupIds($groupIds);

        $_permission = [];
        foreach ($permissions as $permission){
            $_permission[$permission['module']][$permission['module']][] = ['module'=>$permission['module'],'permission'=>$permission['name']];
        }

        return array_values($_permission);
    }

    public function getById($id){
        $user = LinUser::find($id);
        if(!$user){
            throw new NotFoundException(10021);
        }
        return $user;
    }

    public function updateUserInfo($params){
        $user = LocalUser::getLocalUser();
        if(isset($params['username'])){
            $exist = LinUser::where('id','<>',$user->id)
                ->where('username',$params['username'])
                ->count();
            if($exist > 0){
                throw new ForbiddenException(10071);
            }
            $user->username = $params['username'];
        }
        if(isset($params['email']) && $params['email']){
            $exist = LinUser::where('id','<>',$user->id)
                ->where('email',$params['email'])
                ->count();
            if($exist > 0){
                throw new ForbiddenException(10076);
            }
            $user->email = $params['email'];
        }
        if(isset($params['nickname'])){
            $user->nickname = $params['nickname'];
        }

        if(isset($params['avatar'])){
            $user->avatar = $params['avatar'];
        }
        $user->save();
        return $user;
    }

    public function changeUserPassword($params){
        $user = LocalUser::getLocalUser();
        #验证旧密码
        if (!$this->userIdentityService->verifyUsernamePassword($user->id, $user->username, $params['old_password'])) {
            throw new ParameterException(10031);
        }
        $this->userIdentityService->changePassword($user->id,$params['new_password']);
    }

    public function getCaptcha(){
        $config = new Config();
        $code = new VerifyCode($config);
        $result = $code->DrawCode();
        $imgCode = $result->getImageCode();

        $container = ApplicationContext::getContainer();
        $generator = $container->get(IdGeneratorInterface::class);
        $uuid = strval($generator->generate());
        $key = $this->key.$uuid;
        //写入缓存 用于其他方法验证 并且设置过期时间
        $this->redis->set($key,$imgCode,60);
        return [
            'uuid' => $uuid,
            'captcha' => $result->getImageBase64()
        ];

    }

    public function checkCaptcha($uuid,$captcha){
        $key = $this->key.$uuid;
        $captchaCode = $this->redis->get($key);
        if(!$captchaCode){
            throw new ParameterException(10033);
        }elseif(strtolower($captchaCode) == strtolower($captcha)){
            $this->redis->del($key);
        }else{
            throw new ParameterException(10034);
        }
        return true;
    }
}